package gov.nasa.anml.lifted;

import java.util.ArrayList;
import java.util.HashMap;

import gov.nasa.anml.utility.SimpleObject;
import gov.nasa.anml.utility.SimpleString;

public class ScopeImp implements Scope, Cloneable{

	protected Scope parent;
	
	// one namespace by simple id.  Should eventually implement
	// type-signature mapping.
	// well an alternative is also just to have terms have variable args
	// (be their own type signature mappings)
	public HashMap<SimpleString,Identifier<?,?>> symbols = new HashMap<SimpleString,Identifier<?,?>>();

	public SymbolTable<Type<?>> types = new SymbolTable<Type<?>>();
	public SymbolTable<Constant<?>> constants = new SymbolTable<Constant<?>>();
	public SymbolTable<ConstantFunction<?>> constantFunctions = new SymbolTable<ConstantFunction<?>>();
	public SymbolTable<Fluent<?>> fluents = new SymbolTable<Fluent<?>>();
	public SymbolTable<FluentFunction<?>> fluentFunctions = new SymbolTable<FluentFunction<?>>();
	public SymbolTable<Parameter<?>> parameters = new SymbolTable<Parameter<?>>();
	public SymbolTable<LabeledInterval<?,?>> labels = new SymbolTable<LabeledInterval<?,?>>();

	// voluntary
	public SymbolTable<Action> actions = new SymbolTable<Action>(); 

	// compulsory
	public ArrayList<Statement> statements = new ArrayList<Statement>(); 

	// disjunctive compulsion
	public ArrayList<Decomposition> decompositions = new ArrayList<Decomposition>();


	
	public ScopeImp clone() {
		ScopeImp ret = null;
		try {
			ret = (ScopeImp) super.clone();
		} catch (CloneNotSupportedException e) {
			//assert false;
		}
		ret.symbols = new HashMap<SimpleString,Identifier<?,?>>();
		ret.types = new SymbolTable<Type<?>>();
		ret.constants = new SymbolTable<Constant<?>>();
		ret.constantFunctions = new SymbolTable<ConstantFunction<?>>();
		ret.fluents = new SymbolTable<Fluent<?>>();
		ret.fluentFunctions = new SymbolTable<FluentFunction<?>>();
		ret.parameters = new SymbolTable<Parameter<?>>();

		ret.actions = new SymbolTable<Action>(); 
		ret.statements = new ArrayList<Statement>(); 
		ret.decompositions = new ArrayList<Decomposition>(); 
		
		ret.inherit(this);
		return ret;
	}
	
	// numbering will be off unless there is a sole inheritance preceding all extension of the scope.
	// inheritance is object/class inheritance.  nesting is different; that is handled through the parent pointer
	public void inherit(ScopeImp s) {
		symbols.putAll(s.symbols);
		parameters.inherit(s.parameters);
		types.inherit(s.types);
		constants.inherit(s.constants);
		constantFunctions.inherit(s.constantFunctions);
		fluents.inherit(s.fluents);
		fluentFunctions.inherit(s.fluentFunctions);

		actions.inherit(s.actions);
		statements.addAll(s.statements);
		decompositions.addAll(s.decompositions);
		
	}

	public ScopeImp() {}
	public ScopeImp(Scope parent) {init(parent);}
	public final void init(Scope parent) {
		this.parent = parent;
	}
	
	public Action addAction(Action a) {
		if (a == null)
			return null;
		symbols.put(a.name,a);
		return actions.put(a);
	}

	public Statement addStatement(Statement s) {
		if(s==null)
			return null;
		if(statements.add(s))
			return s;
		return null;
	}
	
	public Identifier<?,?> resolveSymbol(SimpleString n) {
		Identifier<?,?> s=symbols.get(n);
		if (s==null)
			return parent == null ? null : parent.resolveSymbol(n);
		return s;
	}
	
	public Identifier<?, ?> addSymbol(IdentifierImp<?,?> s) {
		return symbols.put(s.name,s);
	}
	
	public Identifier<?, ?> addSymbol(Identifier<?,?> s) {
		return symbols.put(s.name(),s);
	}
	
	public Identifier<?, ?> getSymbol(SimpleString s) {
		return symbols.get(s);
	}


	public <T extends SimpleObject<? super T>> Parameter<T> addParameter(Parameter<T> t) {
		symbols.put(t.name,t);
		return parameters.put(t);
	}
	
	public <T extends SimpleObject<? super T>> Type<T> addType(Type<T> t) {
		symbols.put(t.name(),t);
		return types.put(t);
	}

	public <T extends SimpleObject<? super T>> Constant<T> addConstant(Constant<T> c) {
		symbols.put(c.name,c);
		return constants.put(c);
	}
	
	public <T extends SimpleObject<? super T>> LabeledInterval addLabel(LabeledInterval l) {
		symbols.put(l.name(),l);
		return labels.put(l);
	}

	public <T extends SimpleObject<? super T>> ConstantFunction<T> addConstantFunction(ConstantFunction<T> c) {
		symbols.put(c.name,c);
		return constantFunctions.put(c);
	}

	public <T extends SimpleObject<? super T>> Fluent<T> addFluent(Fluent<T> f) {
		symbols.put(f.name,f);
		return fluents.put(f);
	}

	public <T extends SimpleObject<? super T>> FluentFunction<T> addFluentFunction(FluentFunction<T> f) {
		symbols.put(f.name,f);
		return fluentFunctions.put(f);
	}

	public SimpleString getParameterName(int p) {
		return parameters.getName(p);
	}
	
	public SimpleString getTypeName(int t) {
		return types.getName(t);
	}

	public SimpleString getConstantName(int c) {
		return constants.getName(c);
	}
	
	public SimpleString getConstantFunctionName(int c) {
		return constantFunctions.getName(c);
	}

	public SimpleString getFluentName(int f) {
		return fluents.getName(f);
	}

	public SimpleString getFluentFunctionName(int f) {
		return fluentFunctions.getName(f);
	}

	public SimpleString getActionName(int a) {
		return actions.getName(a);
	}

	public Parameter<?> getParameter(int p) {
		return parameters.get(p);
	}
	
	public Type<?> getType(int t) {
		return types.get(t);
	}

	public Constant<?> getConstant(int c) {
		return constants.get(c);
	}

	public ConstantFunction<?> getConstantFunction(int c) {
		return constantFunctions.get(c);
	}

	public Fluent<?> getFluent(int f) {
		return fluents.get(f);
	}

	public FluentFunction<?> getFluentFunction(int f) {
		return fluentFunctions.get(f);
	}

	public Action getAction(int a) {
		return actions.get(a);
	}

	public Parameter<?> getParameter(SimpleString p) {
		return parameters.get(p);
	}
	
	public Type<?> getType(SimpleString t) {
		return types.get(t);
	}

	public Constant<?> getConstant(SimpleString c) {
		return constants.get(c);
	}

	public ConstantFunction<?> getConstantFunction(SimpleString c) {
		return constantFunctions.get(c);
	}

	public Fluent<?> getFluent(SimpleString f) {
		return fluents.get(f);
	}
	
	public LabeledInterval getLabel(SimpleString l) {
		return labels.get(l);
	}
	
	public FluentFunction<?> getFluentFunction(SimpleString f) {
		return fluentFunctions.get(f);
	}

	public Action getAction(SimpleString a) {
		return actions.get(a);
	}

	public Action resolveAction(SimpleString n) {
		Action s = actions.get(n);
		if (s == null)
			return parent == null ? null : parent.resolveAction(n);
		return s;
	}

	public Constant<?> resolveConstant(SimpleString n) {
		Constant<?> s = constants.get(n);
		if (s == null)
			return parent == null ? null : parent.resolveConstant(n);
		return s;
	}

	public ConstantFunction<?> resolveConstantFunction(SimpleString n) {
		ConstantFunction<?> s = constantFunctions.get(n);
		if (s == null)
			return parent == null ? null : parent.resolveConstantFunction(n);
		return s;
	}

	public Fluent<?> resolveFluent(SimpleString n) {
		Fluent<?> s = fluents.get(n);
		if (s == null)
			return parent == null ? null : parent.resolveFluent(n);
		return s;
	}

	public FluentFunction<?> resolveFluentFunction(SimpleString n) {
		FluentFunction<?> s = fluentFunctions.get(n);
		if (s == null)
			return parent == null ? null : parent.resolveFluentFunction(n);
		return s;
	}

	public Parameter<?> resolveParameter(SimpleString n) {
		Parameter<?> s = parameters.get(n);
		if (s == null)
			return parent == null ? null : parent.resolveParameter(n);
		return s;
	}

	public Type<?> resolveType(SimpleString n) {
		Type<?> s = types.get(n);
		if (s == null) {
			if (parent == this) {
				System.err.println("Circular scopes?");
				return null;
			}
			return parent == null ? null : parent.resolveType(n);
		}
		return s;
	}

	public Scope getParent() {
		return parent;
	}

	public void setParent(Scope parent) {
		if (parent != this)
			this.parent = parent;
		else
			System.err.println("Setting circular types");
	}

	public void addDecomposition(Decomposition d) {
		if (d != null)
			decompositions.add(d);
	}


}
